﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace Biosek
{
	public static class SamplerHelper
	{
		/// <summary>
		/// Muestrea una secuencia usando ventana deslizante
		/// </summary>
		/// <param name="seq">Secuencia</param>
		/// <param name="windowLength">Longitud de la ventana</param>
		/// <param name="positiveIndex">Indice positivo, el indice es una posicion basada en cero</param>
		/// <param name="targetNegativeCollection">Coleccion de secuencias negativas</param>
		/// <param name="targetPositiveCollection">Coleccion de secuencias positivas</param>
		public static void SampleWithSlidingWindow<T>(this IList<T> seq, int windowLength, int positiveIndex, ICollection<T[]> targetNegativeCollection, ICollection<T[]> targetPositiveCollection)
		{
			SampleWithSlidingWindow(seq, 0, seq.Count-1, windowLength, positiveIndex, targetNegativeCollection, targetPositiveCollection);			
		}

		/// <summary>
		/// Muestrea una secuencia usando ventana deslizante.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="seq">Secuencia</param>
		/// <param name="indexBegin">Indice donde el empieza el muestreo (inclusivo)</param>
		/// <param name="indexEnd">Indice donde termina el muestreo (inclusivo)</param>
		/// <param name="windowLength">Longitud de la ventana</param>
		/// <param name="positiveIndex">Indice de la muestra positiva</param>
		/// <param name="targetNegativeCollection">Coleccion donde se añaden las muestras negativas</param>
		/// <param name="targetPositiveCollection">Coleccion donde se añade la muestra positiva</param>
		public static void SampleWithSlidingWindow<T>(this IList<T> seq, int indexBegin, int indexEnd, int windowLength, int positiveIndex, ICollection<T[]> targetNegativeCollection, ICollection<T[]> targetPositiveCollection)
		{
			for (int i = indexBegin; i < indexEnd-windowLength+1; i++)
			{
				var item = seq.Skip(i).Take(windowLength).ToArray();
				if (i == positiveIndex) targetPositiveCollection.Add(item);
				else targetNegativeCollection.Add(item);
			}
		}

		/// <summary>
		/// Construye un enumerable donde antepone un numero a cada muestra para indicar su clase.
		/// </summary>
		/// <typeparam name="T">Tipo de las secuencias de entrada, debe ser un tipo de entero</typeparam>
		/// <returns>Una sola secuencia</returns>
		public static IEnumerable<IEnumerable<T>> TagAndConcat<T>(params IEnumerable<IEnumerable<T>>[] samplesPerClass)
		{
			for (int i = 0; i < samplesPerClass.Length; i++)
			{
				var data = samplesPerClass[i];
				var tag = new T[] { (T)Convert.ChangeType(i, typeof(T)) };
				foreach (var item in data)
				{
					yield return Enumerable.Concat(tag, item);
				}
			}
		}

		/// <summary>
		/// Agrupa las secuencias por clase. Se interpreta el primer elemento de cada secuencia como la clase,
		/// eliminando ese elemento de cada clase
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="data"></param>
		/// <returns></returns>
		public static IDictionary<T, IEnumerable<IEnumerable<T>>> GroupByClass<T>(IEnumerable<IEnumerable<T>> data)
		{
			return data.GroupBy(x => x.First(), x => x.Skip(1)).ToDictionary(x => x.Key, x => x.AsEnumerable());
		}

		/// <summary>
		/// Aplica ordenamiento aleatorio a un conjunto de secuencias conservando su relacion de posicion
		/// </summary>
		/// <param name="r"></param>
		/// <param name="col">Conjunto de listas, Todas deben tener la misma cantidad de elementos</param>
		public static void Shuffle<T>(Random r, params IList<T>[] col)
		{
			var count = col.First().Count;
			foreach (var item in col)
			{
				if (item.Count != count) throw new InvalidOperationException("Shufle solo funciona con elementos iguales");
			}
			for (int i = 0; i < count; i++)
			{
				var o = r.Next(count);
				foreach (var item in col)
				{
					var sw = item[i];
					item[i] = item[o];
					item[o] = sw;
				}
			}
		}

		/// <summary>
		/// Aplica ordenamiento aleatorio a una secuencia
		/// </summary>
		public static IList<T> Shuffle<T>(this IEnumerable<T> col, Random rand)
		{
			var l = col.ToList();
			Shuffle(rand, l);
			return l;
		}
	}
}
